/**
*	@class Vector4
*
*	@brief This class implements a 4D Vector object.
*
*	@author William McVicar
*/

#pragma once

#ifndef __VECTOR4_BASIC_H_INCLUDED__
#define __VECTOR4_BASIC_H_INCLUDED__

#pragma warning( disable : 4201 )

// Library Includes

// Local Includes
#include "MathCommon.h"

// Types

// Constants

// Prototypes

namespace Protein
{

template <class Real>
class Vector4
{

public:

  Vector4();
	Vector4( Real x, Real y, Real z );
	Vector4( Real val );
	Vector4( const Vector4& other );
	Vector4( Real* in );
	~Vector4();

  void                    set( Real x, Real y, Real z );
  void                    set( Real val );

  Vector4&                operator=( const Vector4& rhs );

	Vector4	                operator+( const Vector4& rhs ) const;
	Vector4	                operator-( const Vector4& rhs ) const;
	Vector4	                operator*( Real rhs ) const;
	Vector4	                operator/( Real rhs ) const;
	Vector4	                operator-() const;

	Vector4&	              operator+=( const Vector4& rhs );
	Vector4&	              operator-=( const Vector4& rhs );
	Vector4&	              operator*=( Real rhs );
	Vector4&	              operator/=( Real rhs );

	bool			              operator==( const Vector4& rhs ) const;
	bool			              operator!=( const Vector4& rhs ) const;
	bool			              operator> ( const Vector4& rhs ) const;
	bool			              operator< ( const Vector4& rhs ) const;
	bool			              operator>=( const Vector4& rhs ) const;
	bool			              operator<=( const Vector4& rhs ) const;

	Real			              dot( const Vector4& in ) const;
	Vector4		              cross( const Vector4& in ) const;
  Vector4                 unitCross( const Vector4& in ) const;
	Real			              length() const;
	Real			              lengthSqr() const;

	void			              normalise();
	Vector4			            normalised() const;

	Real			              distance( const Vector4& in ) const;
	Real			              distanceSqr( const Vector4& in ) const;

	static Real	            dot( const Vector4& a, const Vector4& b );
	static Vector4	        cross( const Vector4& a, const Vector4& b );
  static Vector4          unitCross( const Vector4& a, const Vector4& b );
	static Real	            length( const Vector4& in );
	static Real	            lengthSqr( const Vector4& in );
	static Real	            distance( const Vector4& a, const Vector4& b );
	static Real	            distanceSqr( const Vector4& a, const Vector4& b );

	static const Vector4    Zero;  
	static const Vector4    i;	    
	static const Vector4    j;	    
	static const Vector4    k;	    
	static const Vector4    negi;	
	static const Vector4    negj;	
	static const Vector4    negk;

  union
	{
		struct
    { 
      Real x;
      Real y;
      Real z;
      Real w;
    };

	  Real m_components[4];
  };
};

template < class Real >
Vector4<Real> operator*( Real val, const Vector4<Real>& in );

template < class Real >
Vector4<Real>::Vector4()
{
	memset( m_components, 0, sizeof( Real ) * 4 );
}

template < class Real >
Vector4<Real>::Vector4( Real _x, Real _y, Real _z )
: x( _x )
, y( _y )
, z( _z )
, w( 1.0 )
{
}

template < class Real >
Vector4<Real>::Vector4( const Vector4<Real>& rhs )
{
	memcpy( m_components, rhs.m_components, sizeof( Real ) * 4 );
}

template < class Real >
Vector4<Real>::Vector4( Real* in )
{
	memcpy( m_components, in, sizeof( Real ) * 4 );
}

template< class Real >
Vector4<Real>::~Vector4()
{
}

template< class Real >
 void Vector4<Real>::set( Real _x, Real _y, Real _z )
{
  x = _x;
	y = _y;
	z = _z;
  w = (Real)1.0;
}

template< class Real >
 void Vector4<Real>::set( Real val )
{
	memset( m_components, val, sizeof( Real ) * 3 );
  w = 1.0;
}

template< class Real >
 Vector4<Real>& Vector4<Real>::operator=( const Vector4& rhs )
{
	memcpy( m_components, rhs.m_components, sizeof( Real ) * 4 );
	return *this;
}

template< class Real >  Vector4<Real>&
Vector4<Real>::operator+=( const Vector4<Real>& rhs )
{
	x += rhs.x;
	y += rhs.y;
	z += rhs.z;

	return *this;
}

template< class Real >  Vector4<Real>&
Vector4<Real>::operator-=( const Vector4<Real>& rhs )
{
	x -= rhs.x;
	y -= rhs.x;
	z -= rhs.z;

	return *this;	
}

template< class Real >  Vector4<Real>&
Vector4<Real>::operator*=( Real rhs )
{
	x *= val;
	y *= val;
	z *= val;

	return *this;	
}

template< class Real >  Vector4<Real>&
Vector4<Real>::operator/=( Real val )
{
	x /= val;
	y /= val;
	z /= val;

	return *this;	
}

template< class Real >  Vector4<Real>
Vector4<Real>::operator+( const Vector4<Real>& rhs ) const
{
	Vector4 temp = *this;

	temp.x += rhs.x;
	temp.y += rhs.y;
	temp.z += rhs.z;

	return temp;
}

template< class Real >  Vector4<Real>
Vector4<Real>::operator-( const Vector4<Real>& rhs ) const
{
	Vector4 temp = *this;

	temp.x -= rhs.x;
	temp.y -= rhs.y;
	temp.z -= rhs.z;

	return temp;	
}

template< class Real >  Vector4<Real>
Vector4<Real>::operator*( const Real val ) const
{
	Vector4 temp = *this;

	temp.x *= val;
	temp.y *= val;
	temp.z *= val;

	return temp;
}

template< class Real >  Vector4<Real>
Vector4<Real>::operator/( const Real val ) const
{
	Vector4 temp = *this;

	temp.x /= val;
	temp.y /= val;
	temp.z /= val;

	return temp;	
}

template< class Real >  Vector4<Real> 
Vector4<Real>::operator -() const
{
	Vector4 temp = *this;

	temp.x = Math<Real>::FlipSign(temp.x);
	temp.y = Math<Real>::FlipSign(temp.y);
	temp.z = Math<Real>::FlipSign(temp.z);

	return temp;
}

template<class Real>  Real
Vector4<Real>::length() const
{
	return( Math<Real>::Sqrt(( x * x ) + ( y * y ) + ( z * z )) );
}

template<class Real>  Real
Vector4<Real>::lengthSqr() const
{
	return (( x * x ) + ( y * y ) + ( z * z ) );
}

template<class Real> void
Vector4<Real>::normalise()
{
	Real fMag = Math<Real>::Sqrt(( x * x ) + ( y * y ) + ( z * z ));

	if( fMag > Math<Real>::ZERO_TOLERANCE )
	{
		Real fInvLength =((Real)1.0/fMag);
		x *= fInvLength;
		y *= fInvLength;
		z *= fInvLength;
	}
	else
	{
		x =(Real)0.0;
		y =(Real)0.0;
		z =(Real)0.0;
	}
}

template<class Real>  Vector4<Real>
Vector4<Real>::normalised() const
{
	Vector4<Real> temp;
	Real fMag = Math<Real>::Sqrt(( x * x ) + ( y * y ) + ( z * z ));

	if( fMag > Math<Real>::ZERO_TOLERANCE )
	{
		Real fInvLength =((Real)1.0/fMag);
		temp.x = x * fInvLength;
		temp.y = y * fInvLength;
		temp.z = z * fInvLength;
	}
	else
	{
		temp.x =(Real)0.0;
		temp.y =(Real)0.0;
		temp.z =(Real)0.0;
	}

	return temp;
}

template < class Real > 
 Real Vector4<Real>::dot(const Vector4<Real>& a, const Vector4<Real>& b)
{
	return (( a.x * b.x ) + ( a.y * b.y ) + ( a.z * b.z ));
}

template < class Real > 
 Real Vector4<Real>::dot(const Vector4<Real>& rhs ) const
{
	return (( x * rhs.x ) + ( y * rhs.y ) + ( z * rhs.z ) );
}

template < class Real > 
 Vector4<Real> Vector4<Real>::cross( const Vector4<Real>& a, const Vector4<Real>& b )
{
	return Vector4( ( ( a.y * b.z ) - ( b.y * a.z ) ),
					        ( ( a.z * b.x ) - ( b.z * a.x ) ),
					        ( ( a.x * b.y ) - ( b.x * a.y ) ) );
}

template < class Real > 
 Vector4<Real> Vector4<Real>::cross( const Vector4<Real>& rhs  ) const
{
	return Vector4((( y * rhs.z ) - ( rhs.y * z ) ),
					       (( z * rhs.x ) - ( rhs.z * x ) ),
					       (( x * rhs.y ) - ( rhs.x * y ) ) );
}

template < class Real >  Real
Vector4<Real>::distance(const Vector4 &start, const Vector4 &dest)
{
	Real fDeltaX = dest.x - start.x;
	Real fDeltaY = dest.y - start.y;
	Real fDeltaZ = dest.z - start.z;

	return( Math<Real>::Sqrt(( fDeltaX * fDeltaX ) + ( fDeltaY * fDeltaY ) + ( fDeltaZ * fDeltaZ ) ) );
}

template < class Real >  Vector4<Real> 
Vector4<Real>::unitCross( const Vector4<Real>& a, const Vector4<Real>& b )
{
   return (a.cross( b )).normalised();
}

template < class Real >  Vector4<Real> 
Vector4<Real>::unitCross( const Vector4<Real>& rhs ) const
{
	Vector4  cross((( y * rhs.z ) - ( rhs.y * z ) ),
					       (( z * rhs.x ) - ( rhs.z * x ) ),
					       (( x * rhs.y ) - ( rhs.x * y ) ) );

	cross.Normalise();
	return cross;
}

template < class Real > bool
Vector4<Real>::operator !=(const Vector4<Real>& rhs) const
{
	return memcmp( m_components, _krVector.m_components, 4 * sizeof( Real ) ) != 0;
}

template < class Real > bool
Vector4<Real>::operator ==(const Vector4& rhs) const
{	
	return memcmp( m_components, rhs.m_components, 4 * sizeof( Real ) ) == 0;
}

template< class Real > bool
Vector4<Real>::operator<( const Vector4& rhs ) const
{
	return memcmp( m_components, rhs.m_components, 4 * sizeof( Real ) ) < 0;
}

template < class Real > bool
Vector4<Real>::operator<=( const Vector4& rhs ) const
{
	return memcmp( m_components, rhs.m_components, 4 * sizeof( Real ) ) <= 0;
}

template < class Real > bool
Vector4<Real>::operator>( const Vector4& rhs ) const
{
	return memcmp( m_components, rhs.m_components, 4 * sizeof( Real ) ) > 0;
}

template < class Real > bool
Vector4<Real>::operator>=( const Vector4& rhs ) const
{
	return memcmp( m_components, rhs.m_components, 4 * sizeof( Real ) ) >= 0;
}

template< class Real > 
Vector4<Real> operator*( Real val, const Vector4<Real>& in )
{
	return Vector4<Real>( in.x * val, in.y * val, in.z * val );
}

typedef Vector4<float> Vector4f;
typedef Vector4<double> Vector4d;

}

#pragma warning( default : 4201 )

#endif // __VECTOR4_BASIC_H_INCLUDED__